// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.popman;

import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TIntHashSet;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import se.krka.kahlua.vm.KahluaTable;
import zombie.ai.states.PathFindState;
import zombie.ai.states.WalkTowardState;
import zombie.characters.IsoPlayer;
import zombie.characters.IsoZombie;
import zombie.core.Core;
import zombie.core.Rand;
import zombie.core.math.PZMath;
import zombie.debug.DebugLog;
import zombie.gameStates.ChooseGameInfo;
import zombie.iso.IsoChunk;
import zombie.iso.IsoDirections;
import zombie.iso.IsoGridSquare;
import zombie.iso.IsoMetaGrid;
import zombie.iso.IsoMovingObject;
import zombie.iso.IsoWorld;
import zombie.network.GameClient;
import zombie.network.GameServer;
import zombie.util.PZXmlParserException;
import zombie.util.PZXmlUtil;
import zombie.util.list.PZArrayUtil;
import zombie.vehicles.PolygonalMap2;

public final class ZombiePopulationManager {
    public static final ZombiePopulationManager instance = new ZombiePopulationManager();
    protected static final int SQUARES_PER_CHUNK = 10;
    protected static final int CHUNKS_PER_CELL = 30;
    protected static final int SQUARES_PER_CELL = 300;
    protected static final byte OLD_ZOMBIE_CRAWLER_CAN_WALK = 1;
    protected static final byte OLD_ZOMBIE_FAKE_DEAD = 2;
    protected static final byte OLD_ZOMBIE_CRAWLER = 3;
    protected static final byte OLD_ZOMBIE_WALKER = 4;
    protected static final int ZOMBIE_STATE_INITIALIZED = 1;
    protected static final int ZOMBIE_STATE_CRAWLING = 2;
    protected static final int ZOMBIE_STATE_CAN_WALK = 4;
    protected static final int ZOMBIE_STATE_FAKE_DEAD = 8;
    protected static final int ZOMBIE_STATE_CRAWL_UNDER_VEHICLE = 16;
    protected int minX;
    protected int minY;
    protected int width;
    protected int height;
    protected boolean bStopped;
    protected boolean bClient;
    private final DebugCommands dbgCommands = new DebugCommands();
    public static boolean bDebugLoggingEnabled = false;
    private final LoadedAreas loadedAreas = new LoadedAreas(false);
    private final LoadedAreas loadedServerCells = new LoadedAreas(true);
    private final PlayerSpawns playerSpawns = new PlayerSpawns();
    private short[] realZombieCount;
    private short[] realZombieCount2;
    private long realZombieUpdateTime = 0L;
    private final ArrayList<IsoZombie> saveRealZombieHack = new ArrayList();
    private final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
    private final TIntHashSet newChunks = new TIntHashSet();
    private final ArrayList<ChooseGameInfo.SpawnOrigin> spawnOrigins = new ArrayList();
    public float[] radarXY;
    public int radarCount;
    public boolean radarRenderFlag;
    public boolean radarRequestFlag;
    private final ArrayList<IsoDirections> m_sittingDirections = new ArrayList();

    ZombiePopulationManager() {
        this.newChunks.setAutoCompactionFactor(0.0F);
    }

    private static native void n_init(boolean var0, boolean var1, int var2, int var3, int var4, int var5);

    private static native void n_config(float var0, float var1, float var2, int var3, float var4, float var5, float var6, float var7, int var8);

    private static native void n_setSpawnOrigins(int[] var0);

    private static native void n_setOutfitNames(String[] var0);

    private static native void n_updateMain(float var0, double var1);

    private static native boolean n_hasDataForThread();

    private static native void n_updateThread();

    private static native boolean n_shouldWait();

    private static native void n_beginSaveRealZombies(int var0);

    private static native void n_saveRealZombies(int var0, ByteBuffer var1);

    private static native void n_save();

    private static native void n_stop();

    private static native void n_addZombie(float var0, float var1, float var2, byte var3, int var4, int var5, int var6, int var7);

    private static native void n_aggroTarget(int var0, int var1, int var2);

    private static native void n_loadChunk(int var0, int var1, boolean var2);

    private static native void n_loadedAreas(int var0, int[] var1, boolean var2);

    protected static native void n_realZombieCount(short var0, short[] var1);

    protected static native void n_spawnHorde(int var0, int var1, int var2, int var3, float var4, float var5, int var6);

    private static native void n_worldSound(int var0, int var1, int var2, int var3);

    private static native int n_getAddZombieCount();

    private static native int n_getAddZombieData(int var0, ByteBuffer var1);

    private static native boolean n_hasRadarData();

    private static native void n_requestRadarData();

    private static native int n_getRadarZombieData(float[] var0);

    private static void noise(String string) {
        if (bDebugLoggingEnabled && (Core.bDebug || GameServer.bServer && GameServer.bDebug)) {
            DebugLog.log("ZPOP: " + string);
        }
    }

    public static void init() {
        String string = "";
        if ("1".equals(System.getProperty("zomboid.debuglibs.popman"))) {
            DebugLog.log("***** Loading debug version of PZPopMan");
            string = "d";
        }

        if (System.getProperty("os.name").contains("OS X")) {
            System.loadLibrary("PZPopMan");
        } else if (System.getProperty("sun.arch.data.model").equals("64")) {
            System.loadLibrary("PZPopMan64" + string);
        } else {
            System.loadLibrary("PZPopMan32" + string);
        }

        zombie.DebugFileWatcher.instance
            .add(
                new zombie.PredicatedFileWatcher(
                    zombie.ZomboidFileSystem.instance.getMessagingDirSub("Trigger_Zombie.xml"), ZombiePopulationManager::onTriggeredZombieFile
                )
            );
    }

    private static void onTriggeredZombieFile(String string) {
        DebugLog.General.println("ZombiePopulationManager.onTriggeredZombieFile(" + string + ">");

        ZombieTriggerXmlFile zombieTriggerXmlFile;
        try {
            zombieTriggerXmlFile = PZXmlUtil.parse(ZombieTriggerXmlFile.class, string);
        } catch (PZXmlParserException pZXmlParserException) {
            System.err.println("ZombiePopulationManager.onTriggeredZombieFile> Exception thrown. " + pZXmlParserException);
            pZXmlParserException.printStackTrace();
            return;
        }

        if (zombieTriggerXmlFile.spawnHorde > 0) {
            processTriggerSpawnHorde(zombieTriggerXmlFile);
        }

        if (zombieTriggerXmlFile.setDebugLoggingEnabled && bDebugLoggingEnabled != zombieTriggerXmlFile.bDebugLoggingEnabled) {
            bDebugLoggingEnabled = zombieTriggerXmlFile.bDebugLoggingEnabled;
            DebugLog.General.println("  bDebugLoggingEnabled: " + bDebugLoggingEnabled);
        }
    }

    private static void processTriggerSpawnHorde(ZombieTriggerXmlFile zombieTriggerXmlFile) {
        DebugLog.General.println("  spawnHorde: " + zombieTriggerXmlFile.spawnHorde);
        if (IsoPlayer.getInstance() != null) {
            IsoPlayer isoPlayer = IsoPlayer.getInstance();
            instance.createHordeFromTo((int)isoPlayer.x, (int)isoPlayer.y, (int)isoPlayer.x, (int)isoPlayer.y, zombieTriggerXmlFile.spawnHorde);
        }
    }

    public void init(IsoMetaGrid metaGrid) {
        this.bClient = GameClient.bClient;
        if (!this.bClient) {
            this.minX = metaGrid.getMinX();
            this.minY = metaGrid.getMinY();
            this.width = metaGrid.getWidth();
            this.height = metaGrid.getHeight();
            this.bStopped = false;
            n_init(this.bClient, GameServer.bServer, this.minX, this.minY, this.width, this.height);
            this.onConfigReloaded();
            String[] string = (String[])zombie.PersistentOutfits.instance.getOutfitNames().toArray(new String[0]);

            for (int _int = 0; _int < string.length; _int++) {
                string[_int] = string[_int].toLowerCase();
            }

            n_setOutfitNames(string);
            TIntArrayList tIntArrayList = new TIntArrayList();

            for (ChooseGameInfo.SpawnOrigin spawnOrigin : this.spawnOrigins) {
                tIntArrayList.add(spawnOrigin.x);
                tIntArrayList.add(spawnOrigin.y);
                tIntArrayList.add(spawnOrigin.w);
                tIntArrayList.add(spawnOrigin.h);
            }

            n_setSpawnOrigins(tIntArrayList.toArray());
        }
    }

    public void onConfigReloaded() {
        zombie.SandboxOptions.ZombieConfig zombieConfig = zombie.SandboxOptions.instance.zombieConfig;
        n_config(
            (float)zombieConfig.PopulationMultiplier.getValue(),
            (float)zombieConfig.PopulationStartMultiplier.getValue(),
            (float)zombieConfig.PopulationPeakMultiplier.getValue(),
            zombieConfig.PopulationPeakDay.getValue(),
            (float)zombieConfig.RespawnHours.getValue(),
            (float)zombieConfig.RespawnUnseenHours.getValue(),
            (float)zombieConfig.RespawnMultiplier.getValue() * 100.0F,
            (float)zombieConfig.RedistributeHours.getValue(),
            zombieConfig.FollowSoundDistance.getValue()
        );
    }

    public void registerSpawnOrigin(int int3, int int2, int int1, int int0, KahluaTable var5) {
        if (int3 >= 0 && int2 >= 0 && int1 >= 0 && int0 >= 0) {
            this.spawnOrigins.add(new ChooseGameInfo.SpawnOrigin(int3, int2, int1, int0));
        }
    }

    public void playerSpawnedAt(int x, int y, int z) {
        this.playerSpawns.addSpawn(x, y, z);
    }

    public void addChunkToWorld(IsoChunk chunk) {
        if (!this.bClient) {
            if (chunk.isNewChunk()) {
                int _int = chunk.wy << 16 | chunk.wx;
                this.newChunks.add(_int);
            }

            n_loadChunk(chunk.wx, chunk.wy, true);
        }
    }

    public void removeChunkFromWorld(IsoChunk chunk) {
        if (!this.bClient) {
            if (!this.bStopped) {
                n_loadChunk(chunk.wx, chunk.wy, false);

                for (int int0 = 0; int0 < 8; int0++) {
                    for (int int1 = 0; int1 < 10; int1++) {
                        for (int int2 = 0; int2 < 10; int2++) {
                            IsoGridSquare isoGridSquare = chunk.getGridSquare(int2, int1, int0);
                            if (isoGridSquare != null && !isoGridSquare.getMovingObjects().isEmpty()) {
                                for (int int3 = 0; int3 < isoGridSquare.getMovingObjects().size(); int3++) {
                                    IsoMovingObject isoMovingObject = (IsoMovingObject)isoGridSquare.getMovingObjects().get(int3);
                                    if (isoMovingObject instanceof IsoZombie) {
                                        IsoZombie isoZombie = (IsoZombie)isoMovingObject;
                                        if ((!GameServer.bServer || !isoZombie.bIndoorZombie) && !isoZombie.isReanimatedPlayer()) {
                                            int int4 = this.getZombieState(isoZombie);
                                            if (int0 != 0
                                                || isoGridSquare.getRoom() != null
                                                || isoZombie.getCurrentState() != WalkTowardState.instance()
                                                    && isoZombie.getCurrentState() != PathFindState.instance()) {
                                                n_addZombie(
                                                    isoZombie.x,
                                                    isoZombie.y,
                                                    isoZombie.z,
                                                    (byte)isoZombie.dir.index(),
                                                    isoZombie.getPersistentOutfitID(),
                                                    int4,
                                                    -1,
                                                    -1
                                                );
                                            } else {
                                                n_addZombie(
                                                    isoZombie.x,
                                                    isoZombie.y,
                                                    isoZombie.z,
                                                    (byte)isoZombie.dir.index(),
                                                    isoZombie.getPersistentOutfitID(),
                                                    int4,
                                                    isoZombie.getPathTargetX(),
                                                    isoZombie.getPathTargetY()
                                                );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                int int5 = chunk.wy << 16 | chunk.wx;
                this.newChunks.remove(int5);
                if (GameServer.bServer) {
                    zombie.MapCollisionData.instance.notifyThread();
                }
            }
        }
    }

    public void virtualizeZombie(IsoZombie realZombie) {
        int _int = this.getZombieState(realZombie);
        n_addZombie(
            realZombie.x,
            realZombie.y,
            realZombie.z,
            (byte)realZombie.dir.index(),
            realZombie.getPersistentOutfitID(),
            _int,
            realZombie.getPathTargetX(),
            realZombie.getPathTargetY()
        );
        realZombie.removeFromWorld();
        realZombie.removeFromSquare();
    }

    private int getZombieState(IsoZombie isoZombie) {
        byte _byte = 1;
        if (isoZombie.isCrawling()) {
            _byte |= 2;
        }

        if (isoZombie.isCanWalk()) {
            _byte |= 4;
        }

        if (isoZombie.isFakeDead()) {
            _byte |= 8;
        }

        if (isoZombie.isCanCrawlUnderVehicle()) {
            _byte |= 16;
        }

        return _byte;
    }

    public void setAggroTarget(int id, int x, int y) {
        n_aggroTarget(id, x, y);
    }

    public void createHordeFromTo(int spawnX, int spawnY, int targetX, int targetY, int count) {
        n_spawnHorde(spawnX, spawnY, 0, 0, (float)targetX, (float)targetY, count);
    }

    public void createHordeInAreaTo(int spawnX, int spawnY, int spawnW, int spawnH, int targetX, int targetY, int count) {
        n_spawnHorde(spawnX, spawnY, spawnW, spawnH, (float)targetX, (float)targetY, count);
    }

    public void addWorldSound(zombie.WorldSoundManager.WorldSound sound, boolean doSend) {
        if (!this.bClient) {
            if (sound.radius >= 50) {
                if (!sound.sourceIsZombie) {
                    int _int = zombie.SandboxOptions.instance.Lore.Hearing.getValue();
                    if (_int == 4) {
                        _int = 2;
                    }

                    float _float = zombie.WorldSoundManager.instance.getHearingMultiplier(_int);
                    n_worldSound(sound.x, sound.y, (int)PZMath.ceil((float)sound.radius * _float), sound.volume);
                }
            }
        }
    }

    private void updateRealZombieCount() {
        if (this.realZombieCount == null || this.realZombieCount.length != this.width * this.height) {
            this.realZombieCount = new short[this.width * this.height];
            this.realZombieCount2 = new short[this.width * this.height * 2];
        }

        Arrays.fill(this.realZombieCount, (short)0);
        ArrayList arrayList = IsoWorld.instance.CurrentCell.getZombieList();

        for (int int0 = 0; int0 < arrayList.size(); int0++) {
            IsoZombie isoZombie = (IsoZombie)arrayList.get(int0);
            int int1 = (int)(isoZombie.x / 300.0F) - this.minX;
            int int2 = (int)(isoZombie.y / 300.0F) - this.minY;
            this.realZombieCount[int1 + int2 * this.width]++;
        }

        short _short = 0;

        for (int int3 = 0; int3 < this.width * this.height; int3++) {
            if (this.realZombieCount[int3] > 0) {
                this.realZombieCount2[_short * 2 + 0] = (short)int3;
                this.realZombieCount2[_short * 2 + 1] = this.realZombieCount[int3];
                _short++;
            }
        }

        n_realZombieCount(_short, this.realZombieCount2);
    }

    public void updateMain() {
        if (!this.bClient) {
            long _long = System.currentTimeMillis();
            n_updateMain(zombie.GameTime.getInstance().getMultiplier(), zombie.GameTime.getInstance().getWorldAgeHours());
            int int0 = 0;
            int int1 = 0;
            int int2 = n_getAddZombieCount();
            int int3 = 0;

            while (int3 < int2) {
                this.byteBuffer.clear();
                int int4 = n_getAddZombieData(int3, this.byteBuffer);
                int3 += int4;

                for (int int5 = 0; int5 < int4; int5++) {
                    float float0 = this.byteBuffer.getFloat();
                    float float1 = this.byteBuffer.getFloat();
                    float float2 = this.byteBuffer.getFloat();
                    IsoDirections isoDirections = IsoDirections.fromIndex(this.byteBuffer.get());
                    int int6 = this.byteBuffer.getInt();
                    int int7 = this.byteBuffer.getInt();
                    int int8 = this.byteBuffer.getInt();
                    int int9 = this.byteBuffer.getInt();
                    int int10 = (int)float0 / 10;
                    int int11 = (int)float1 / 10;
                    int int12 = int11 << 16 | int10;
                    if (this.newChunks.contains(int12)) {
                        IsoGridSquare isoGridSquare = IsoWorld.instance.CurrentCell.getGridSquare((int)float0, (int)float1, (int)float2);
                        if (isoGridSquare != null && isoGridSquare.roomID != -1) {
                            continue;
                        }
                    }

                    if (int8 != -1 && this.loadedAreas.isOnEdge((int)float0, (int)float1)) {
                        int8 = -1;
                        int9 = -1;
                    }

                    if (int8 == -1) {
                        this.addZombieStanding(float0, float1, float2, isoDirections, int6, int7);
                        int0++;
                    } else {
                        this.addZombieMoving(float0, float1, float2, isoDirections, int6, int7, int8, int9);
                        int1++;
                    }
                }
            }

            if (int0 > 0) {
                noise("unloaded -> real " + int2);
            }

            if (int1 > 0) {
                noise("virtual -> real " + int2);
            }

            if (this.radarRenderFlag && this.radarXY != null) {
                if (this.radarRequestFlag) {
                    if (n_hasRadarData()) {
                        this.radarCount = n_getRadarZombieData(this.radarXY);
                        this.radarRenderFlag = false;
                        this.radarRequestFlag = false;
                    }
                } else {
                    n_requestRadarData();
                    this.radarRequestFlag = true;
                }
            }

            this.updateLoadedAreas();
            if (this.realZombieUpdateTime + 5000L < _long) {
                this.realZombieUpdateTime = _long;
                this.updateRealZombieCount();
            }

            if (GameServer.bServer) {
                MPDebugInfo.instance.serverUpdate();
            }

            boolean boolean0 = n_hasDataForThread();
            boolean boolean1 = zombie.MapCollisionData.instance.hasDataForThread();
            if (boolean0 || boolean1) {
                zombie.MapCollisionData.instance.notifyThread();
            }

            this.playerSpawns.update();
        }
    }

    private void addZombieStanding(float float2, float float1, float float0, IsoDirections isoDirections, int int1, int int0) {
        IsoGridSquare isoGridSquare0 = IsoWorld.instance.CurrentCell.getGridSquare((int)float2, (int)float1, (int)float0);
        if (isoGridSquare0 != null && (isoGridSquare0.SolidFloorCached ? isoGridSquare0.SolidFloor : isoGridSquare0.TreatAsSolidFloor())) {
            if (!Core.bLastStand && !this.playerSpawns.allowZombie(isoGridSquare0)) {
                noise("removed zombie near player spawn " + (int)float2 + "," + (int)float1 + "," + (int)float0);
                return;
            }

            zombie.VirtualZombieManager.instance.choices.clear();
            IsoGridSquare isoGridSquare1 = null;
            if (!this.isCrawling(int0) && !this.isFakeDead(int0) && Rand.Next(3) == 0) {
                isoGridSquare1 = this.getSquareForSittingZombie(float2, float1, (int)float0);
            }

            if (isoGridSquare1 != null) {
                zombie.VirtualZombieManager.instance.choices.add(isoGridSquare1);
            } else {
                zombie.VirtualZombieManager.instance.choices.add(isoGridSquare0);
            }

            IsoZombie isoZombie = zombie.VirtualZombieManager.instance.createRealZombieAlways(int1, isoDirections.index(), false);
            if (isoZombie != null) {
                if (isoGridSquare1 != null) {
                    this.sitAgainstWall(isoZombie, isoGridSquare1);
                } else {
                    isoZombie.setX(float2);
                    isoZombie.setY(float1);
                }

                if (this.isFakeDead(int0)) {
                    isoZombie.setHealth(0.5F + Rand.Next(0.0F, 0.3F));
                    isoZombie.sprite = isoZombie.legsSprite;
                    isoZombie.setFakeDead(true);
                } else if (this.isCrawling(int0)) {
                    isoZombie.setCrawler(true);
                    isoZombie.setCanWalk(this.isCanWalk(int0));
                    isoZombie.setOnFloor(true);
                    isoZombie.setFallOnFront(true);
                    isoZombie.walkVariant = "ZombieWalk";
                    isoZombie.DoZombieStats();
                }

                if (this.isInitialized(int0)) {
                    isoZombie.setCanCrawlUnderVehicle(this.isCanCrawlUnderVehicle(int0));
                } else {
                    this.firstTimeLoaded(isoZombie, int0);
                }
            }
        } else {
            noise("real -> unloaded");
            n_addZombie(float2, float1, float0, (byte)isoDirections.index(), int1, int0, -1, -1);
        }
    }

    private IsoGridSquare getSquareForSittingZombie(float float1, float float0, int int2) {
        byte _byte = 3;

        for (int int0 = -_byte; int0 < _byte; int0++) {
            for (int int1 = -_byte; int1 < _byte; int1++) {
                IsoGridSquare isoGridSquare = IsoWorld.instance.CurrentCell.getGridSquare((int)float1 + int0, (int)float0 + int1, int2);
                if (isoGridSquare != null && isoGridSquare.isFree(true) && isoGridSquare.getBuilding() == null) {
                    int int3 = isoGridSquare.getWallType();
                    if (int3 != 0
                        && !PolygonalMap2.instance
                            .lineClearCollide(float1, float0, (float)isoGridSquare.x + 0.5F, (float)isoGridSquare.y + 0.5F, isoGridSquare.z, null, false, true)
                        )
                     {
                        return isoGridSquare;
                    }
                }
            }
        }

        return null;
    }

    public void sitAgainstWall(IsoZombie zombie, IsoGridSquare square) {
        float float0 = (float)square.x + 0.5F;
        float float1 = (float)square.y + 0.5F;
        zombie.setX(float0);
        zombie.setY(float1);
        zombie.setSitAgainstWall(true);
        int int0 = square.getWallType();
        if (int0 != 0) {
            this.m_sittingDirections.clear();
            if ((int0 & 1) != 0 && (int0 & 4) != 0) {
                this.m_sittingDirections.add(IsoDirections.SE);
            }

            if ((int0 & 1) != 0 && (int0 & 8) != 0) {
                this.m_sittingDirections.add(IsoDirections.SW);
            }

            if ((int0 & 2) != 0 && (int0 & 4) != 0) {
                this.m_sittingDirections.add(IsoDirections.NE);
            }

            if ((int0 & 2) != 0 && (int0 & 8) != 0) {
                this.m_sittingDirections.add(IsoDirections.NW);
            }

            if ((int0 & 1) != 0) {
                this.m_sittingDirections.add(IsoDirections.S);
            }

            if ((int0 & 2) != 0) {
                this.m_sittingDirections.add(IsoDirections.N);
            }

            if ((int0 & 4) != 0) {
                this.m_sittingDirections.add(IsoDirections.E);
            }

            if ((int0 & 8) != 0) {
                this.m_sittingDirections.add(IsoDirections.W);
            }

            IsoDirections isoDirections = PZArrayUtil.pickRandom(this.m_sittingDirections);
            if (GameClient.bClient) {
                int int1 = (square.x & 1) + (square.y & 1);
                isoDirections = (IsoDirections)this.m_sittingDirections.get(int1 % this.m_sittingDirections.size());
            }

            zombie.setDir(isoDirections);
            zombie.setForwardDirection(isoDirections.ToVector());
            if (zombie.getAnimationPlayer() != null) {
                zombie.getAnimationPlayer().SetForceDir(zombie.getForwardDirection());
            }
        }
    }

    private void addZombieMoving(float float2, float float1, float float0, IsoDirections isoDirections, int int0, int int1, int int3, int int2) {
        IsoGridSquare isoGridSquare = IsoWorld.instance.CurrentCell.getGridSquare((int)float2, (int)float1, (int)float0);
        if (isoGridSquare != null && (isoGridSquare.SolidFloorCached ? isoGridSquare.SolidFloor : isoGridSquare.TreatAsSolidFloor())) {
            if (!Core.bLastStand && !this.playerSpawns.allowZombie(isoGridSquare)) {
                noise("removed zombie near player spawn " + (int)float2 + "," + (int)float1 + "," + (int)float0);
                return;
            }

            zombie.VirtualZombieManager.instance.choices.clear();
            zombie.VirtualZombieManager.instance.choices.add(isoGridSquare);
            IsoZombie isoZombie = zombie.VirtualZombieManager.instance.createRealZombieAlways(int0, isoDirections.index(), false);
            if (isoZombie != null) {
                isoZombie.setX(float2);
                isoZombie.setY(float1);
                if (this.isCrawling(int1)) {
                    isoZombie.setCrawler(true);
                    isoZombie.setCanWalk(this.isCanWalk(int1));
                    isoZombie.setOnFloor(true);
                    isoZombie.setFallOnFront(true);
                    isoZombie.walkVariant = "ZombieWalk";
                    isoZombie.DoZombieStats();
                }

                if (this.isInitialized(int1)) {
                    isoZombie.setCanCrawlUnderVehicle(this.isCanCrawlUnderVehicle(int1));
                } else {
                    this.firstTimeLoaded(isoZombie, int1);
                }

                if (Math.abs((float)int3 - float2) > 1.0F || Math.abs((float)int2 - float1) > 1.0F) {
                    isoZombie.AllowRepathDelay = -1.0F;
                    isoZombie.pathToLocation(int3, int2, 0);
                }
            }
        } else {
            noise("real -> virtual " + float2 + "," + float1);
            n_addZombie(float2, float1, float0, (byte)isoDirections.index(), int0, int1, int3, int2);
        }
    }

    private boolean isInitialized(int _int) {
        return (_int & 1) != 0;
    }

    private boolean isCrawling(int _int) {
        return (_int & 2) != 0;
    }

    private boolean isCanWalk(int _int) {
        return (_int & 4) != 0;
    }

    private boolean isFakeDead(int _int) {
        return (_int & 8) != 0;
    }

    private boolean isCanCrawlUnderVehicle(int _int) {
        return (_int & 16) != 0;
    }

    private void firstTimeLoaded(IsoZombie var1, int var2) {
    }

    public void updateThread() {
        n_updateThread();
    }

    public boolean shouldWait() {
        synchronized (zombie.MapCollisionData.instance.renderLock) {
            return n_shouldWait();
        }
    }

    public void updateLoadedAreas() {
        if (this.loadedAreas.set()) {
            n_loadedAreas(this.loadedAreas.count, this.loadedAreas.areas, false);
        }

        if (GameServer.bServer && this.loadedServerCells.set()) {
            n_loadedAreas(this.loadedServerCells.count, this.loadedServerCells.areas, true);
        }
    }

    public void dbgSpawnTimeToZero(int cellX, int cellY) {
        if (!this.bClient || GameClient.connection.accessLevel == 32) {
            this.dbgCommands.SpawnTimeToZero(cellX, cellY);
        }
    }

    public void dbgClearZombies(int cellX, int cellY) {
        if (!this.bClient || GameClient.connection.accessLevel == 32) {
            this.dbgCommands.ClearZombies(cellX, cellY);
        }
    }

    public void dbgSpawnNow(int cellX, int cellY) {
        if (!this.bClient || GameClient.connection.accessLevel == 32) {
            this.dbgCommands.SpawnNow(cellX, cellY);
        }
    }

    public void beginSaveRealZombies() {
        if (!this.bClient) {
            this.saveRealZombieHack.clear();

            for (IsoZombie isoZombie0 : IsoWorld.instance.CurrentCell.getZombieList()) {
                if (!isoZombie0.isReanimatedPlayer() && (!GameServer.bServer || !isoZombie0.bIndoorZombie)) {
                    this.saveRealZombieHack.add(isoZombie0);
                }
            }

            int int0 = this.saveRealZombieHack.size();
            n_beginSaveRealZombies(int0);
            int int1 = 0;

            while (int1 < int0) {
                this.byteBuffer.clear();
                int int2 = 0;

                while (int1 < int0) {
                    int int3 = this.byteBuffer.position();
                    IsoZombie isoZombie1 = (IsoZombie)this.saveRealZombieHack.get(int1++);
                    this.byteBuffer.putFloat(isoZombie1.x);
                    this.byteBuffer.putFloat(isoZombie1.y);
                    this.byteBuffer.putFloat(isoZombie1.z);
                    this.byteBuffer.put((byte)isoZombie1.dir.index());
                    this.byteBuffer.putInt(isoZombie1.getPersistentOutfitID());
                    int int4 = this.getZombieState(isoZombie1);
                    this.byteBuffer.putInt(int4);
                    int2++;
                    int int5 = this.byteBuffer.position() - int3;
                    if (this.byteBuffer.position() + int5 > this.byteBuffer.capacity()) {
                        break;
                    }
                }

                n_saveRealZombies(int2, this.byteBuffer);
            }

            this.saveRealZombieHack.clear();
        }
    }

    public void endSaveRealZombies() {
        if (!this.bClient) {
            ;
        }
    }

    public void save() {
        if (!this.bClient) {
            n_save();
        }
    }

    public void stop() {
        if (!this.bClient) {
            this.bStopped = true;
            n_stop();
            this.loadedAreas.clear();
            this.newChunks.clear();
            this.spawnOrigins.clear();
            this.radarXY = null;
            this.radarCount = 0;
            this.radarRenderFlag = false;
            this.radarRequestFlag = false;
        }
    }
}
